package game;

import java.awt.*;
import java.util.*;

public class TaiwanChessGame extends ChineseChessBasedGame {
	private final Map<Point, TWChess> chessMap = new HashMap<Point, TWChess>();
	private final Color lineColor = Color.black;
	private final Color boardColor = Color.white;
	private final Color player0Color = Color.black;
	private final Color player1Color = Color.red;
	private int player0ChessCount;
	private int player1ChessCount;
	private boolean firstTurn;

	public TaiwanChessGame() {
		super();
	}

	@Override
	public void Init() {
		listener.OnPlayerColorChange(0, player0Color);
		listener.OnPlayerColorChange(1, player1Color);
		TWChess[] chesses = getAllChess();
		Point[] locations = getShuffledLocations();
		assert chesses.length == locations.length;
		clearChessMap();
		for(int i = 0; i<chesses.length; i++)
		{
			chessMap.put(locations[i], chesses[i]);
		}
		player0ChessCount = player1ChessCount = chesses.length/2;
		firstTurn = true;
	}

	@Override
	public Point GetBoardSize() {
		return new Point(4, 8);
	}

	@Override
	public boolean IsActionableWithoutTarget(Point source) {
		if (HasChess(source)) {
			return chessMap.get(source).IsActionableWithoutTarget();
		} else {
			return true;
		}
	}

	@Override
	public void Action(Point source) {
		if (HasChess(source) && isCoveredChess(source)) {
			chessMap.get(source).TurnOver();
			if(!firstTurn)
			{
				NextPlayer();
			}
			else{
				if(chessMap.get(source).owner == GetCurrentPlayer()){
					System.out.println("match color of "+chessMap.get(source).owner+" and "+GetCurrentPlayer());
					NextPlayer();
				}
				firstTurn = false;
			}
		}
	}

	@Override
	public void Action(Point source, Point target) {
		assert HasChess(source)
				&& GetValidTargets(source).contains(target);
		if (HasChess(target) && WillGameOver(target)) {
			listener.OnGameOver();
		} else {
			chessMap.put(target, chessMap.get(source));
			chessMap.remove(source);
			NextPlayer();
		}
	}

	@Override
	public Set<Point> GetValidTargets(Point source) {
		assert HasChess(source);
		return chessMap.get(source).GetValidTarget(source);
	}

	@Override
	public void PaintLocation(Graphics g, Point size, Point location) {
		g.setColor(boardColor);
		g.fillRect(0, 0, size.x, size.y);
		g.setColor(lineColor);
		g.drawRect(0, 0, size.x, size.y);
		if (HasChess(location)) {
			chessMap.get(location).Paint(g, size);
		}
	}
	@Override
	public Color getPlayerColor(int owner) {
		if(owner == 0)
			return player0Color;
		else
			return player1Color;
	}

	@Override
	boolean HasChess(Point location) {
		return chessMap.containsKey(location);
	}

	@Override
	int GetOwner(Point location) {
		if (HasChess(location))
			return chessMap.get(location).owner;
		return -1;
	}

	@Override
	Class<? extends TWChess> getChessType(Point location) {
		return chessMap.get(location).getClass();
	}
	
	boolean isCoveredChess(Point target){
		assert HasChess(target);
		return chessMap.get(target).IsCovered();
	}
	private boolean WillGameOver(Point finalTarget) {
		assert HasChess(finalTarget);
		if(chessMap.get(finalTarget).owner != GetCurrentPlayer())
		{
			if(chessMap.get(finalTarget).owner == 0)
				player0ChessCount--;
			else
				player1ChessCount--;
				
		}
		if(player0ChessCount == 0 || player1ChessCount == 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	private Point[] getShuffledLocations()
	{
		ArrayList<Point> locations = new ArrayList<Point>(GetBoardSize().x*GetBoardSize().y);
		for(int x = 0; x<GetBoardSize().x; x++)
		{
			for(int y = 0; y<GetBoardSize().y; y++)
			{
				locations.add(new Point(x,y));
			}
		}
		Collections.shuffle(locations);
		Point[] points = new Point[locations.size()];
		return locations.toArray(new Point[locations.size()]);
	}
	
	private TWChess[] getAllChess()
	{
		return new TWChess[]{
				//add Player0's chess(black side)
				new TWCannonChess(0, this),
				new TWCannonChess(0, this),
				new TWHorseChess(0, this),
				new TWHorseChess(0, this),
				new TWChariotChess(0, this),
				new TWChariotChess(0, this),
				new TWElephantChess(0, this),
				new TWElephantChess(0, this),
				new TWAdvisorChess(0, this),
				new TWAdvisorChess(0, this),
				new TWGeneralChess(0, this),
				new TWSoldierChess(0, this),
				new TWSoldierChess(0, this),
				new TWSoldierChess(0, this),
				new TWSoldierChess(0, this),
				new TWSoldierChess(0, this),
				//add Player1's chess(red side)
				new TWCannonChess(1, this),
				new TWCannonChess(1, this),
				new TWHorseChess(1, this),
				new TWHorseChess(1, this),
				new TWChariotChess(1, this),
				new TWChariotChess(1, this),
				new TWElephantChess(1, this),
				new TWElephantChess(1, this),
				new TWAdvisorChess(1, this),
				new TWAdvisorChess(1, this),
				new TWGeneralChess(1, this),
				new TWSoldierChess(1, this),
				new TWSoldierChess(1, this),
				new TWSoldierChess(1, this),
				new TWSoldierChess(1, this),
				new TWSoldierChess(1, this),
			};
	}
	
	private void clearChessMap()
	{
		assert chessMap != null;
		for(Map.Entry<Point, TWChess> e : chessMap.entrySet())
		{
			e.getValue().dispose();
		}
		chessMap.clear();
	}
}

abstract class TWChess extends ChineseChess {
	private static ArrayList<Class<? extends TWChess>> chessOrder = new ArrayList<Class<? extends TWChess>>() {
		{
			add(TWSoldierChess.class);
			add(TWCannonChess.class);
			add(TWHorseChess.class);
			add(TWChariotChess.class);
			add(TWElephantChess.class);
			add(TWAdvisorChess.class);
			add(TWGeneralChess.class);
		}
	};
	private ChineseChess delegateChess;
	private boolean covered = true;

	protected TWChess(int owner, TaiwanChessGame game, ChineseChess delegateChess) {
		super(owner, game);
		this.delegateChess = delegateChess;
	}

	@Override
	public void Paint(Graphics g, Point size) {
		if(!covered)
			super.Paint(g, size);
		else
		{
			g.setColor(Color.green);
			g.fillOval(0, 0, size.x, size.y);
		}
	}

	@Override
	public String getName() {
		assert delegateChess != null;
		return delegateChess.getName();
	}

	public Set<Point> GetValidTarget(Point source) {
		Set<Point> validTargets = new HashSet<Point>();
		AddValidTarget(validTargets, source, new Point(0, 1));
		AddValidTarget(validTargets, source, new Point(0, -1));
		AddValidTarget(validTargets, source, new Point(1, 0));
		AddValidTarget(validTargets, source, new Point(-1, 0));
		RemoveCoveredTargets(validTargets);
		return validTargets;
	}

	public boolean IsActionableWithoutTarget() {
		if(covered){
			return true;
		}
		else{
			return owner != getGame().GetCurrentPlayer();
		}
	}

	public void TurnOver() {
		covered = false;
	}

	@Override
	void dispose() {
		delegateChess = null;
		super.dispose();
	}

	protected boolean IsCovered() {
		return covered;
	}

	protected boolean CanEat(Class<? extends ChineseChess> targetType) {
		return chessOrder.indexOf(this.getClass()) >= chessOrder
				.indexOf(targetType);
	}
	protected ChineseChess GetDelegateChess()
	{
		return delegateChess;
	}
	protected void RemoveCoveredTargets(Set<Point> targets){
		Set<Point> remove = new HashSet<Point>();
		for(Point target : targets){
			if(getGame().HasChess(target) && ((TaiwanChessGame)getGame()).isCoveredChess(target))
			{
				remove.add(target);
			}
		}
		for(Point target : remove){
			targets.remove(target);
		}
	}

	private void AddValidTarget(Set<Point> targets, Point testPoint,
			Point direction) {
		Point nextStep = new Point(testPoint.x + direction.x, testPoint.y
				+ direction.y);
		if (isPlaceableTarget(nextStep) && (!getGame().HasChess(nextStep) || CanEat(getGame().getChessType(nextStep)))) {
			targets.add(nextStep);
		}
	}
	
	
}

class TWChariotChess extends TWChess {

	protected TWChariotChess(int owner, TaiwanChessGame game) {
		super(owner, game, new ChariotChess(owner, game));
	}
}

class TWHorseChess extends TWChess {

	protected TWHorseChess(int owner, TaiwanChessGame game) {
		super(owner, game, new HorseChess(owner, game));
	}
}

class TWElephantChess extends TWChess {

	protected TWElephantChess(int owner, TaiwanChessGame game) {
		super(owner, game, new ElephantChess(owner, game));
	}
}

class TWAdvisorChess extends TWChess {

	protected TWAdvisorChess(int owner, TaiwanChessGame game) {
		super(owner, game, new AdvisorChess(owner, game));
	}
}

class TWGeneralChess extends TWChess {

	protected TWGeneralChess(int owner, TaiwanChessGame game) {
		super(owner, game, new GeneralChess(owner, game));
	}
}

class TWCannonChess extends TWChess {

	protected TWCannonChess(int owner, TaiwanChessGame game) {
		super(owner, game, new CannonChess(owner, game));
	}

	@Override
	protected boolean CanEat(Class<? extends ChineseChess> targetType) {
		return false;
	}

	@Override
	public Set<Point> GetValidTarget(Point source) {
		Set<Point> validTargets = super.GetValidTarget(source);
		addValidTargets(validTargets, source, new Point(0, 1));
		addValidTargets(validTargets, source, new Point(0, -1));
		addValidTargets(validTargets, source, new Point(1, 0));
		addValidTargets(validTargets, source, new Point(-1, 0));
		RemoveCoveredTargets(validTargets);
		return validTargets;
	}

	private void addValidTargets(Set<Point> targets, Point source,
			Point direction) {
		Point currentLocation = nextStep(source,direction);
		Point springboard;
		while(true)
		{
			if(getGame().InRange(currentLocation))
			{
				if(getGame().HasChess(currentLocation))
				{
					springboard = currentLocation;
					break;
				}
				else
				{
					currentLocation = nextStep(currentLocation,direction);
				}
			}
			else
			{
				return;
			}
		}
		addEatableTarget(targets,nextStep(springboard,direction),direction);
	}
	
	private void addEatableTarget(Set<Point> targets,Point testPoint,Point direction)
	{
		if(isPlaceableTarget(testPoint))
		{
			if(getGame().HasChess(testPoint))
			{
				targets.add(testPoint);
			}
			else
			{
				addEatableTarget(targets,nextStep(testPoint,direction),direction);
			}
		}
	}
}

class TWSoldierChess extends TWChess {

	protected TWSoldierChess(int owner, TaiwanChessGame game) {
		super(owner, game, new SoldierChess(owner, game));
	}

	@Override
	protected boolean CanEat(Class<? extends ChineseChess> targetType) {
		if (targetType != TWGeneralChess.class)
			return super.CanEat(targetType);
		else
			return true;
	}

}
